package location

import (
	"context"
	"device-admin/config/mysql"
	"device-admin/internal/dal"
	"device-admin/internal/dao"
	"device-admin/internal/define"
	"device-admin/internal/models"
	jieba "device-admin/internal/pkg/jieba"
	"io"
	"unicode/utf8"

	"192.168.1.75/go-pkg/errorx"
	"192.168.1.75/go-pkg/helper"
	"github.com/samber/lo"
	"github.com/xuri/excelize/v2"
)

type LocalQA struct{}

// AddLib 新增词库
func (LocalQA) AddLib(ctx context.Context, name, merchantID string) (string, error) {
	return dao.LocalQALib{}.Add(ctx, dal.Use(mysql.NewDB()), name, merchantID)
}

// UpdateLib 编辑词库
func (LocalQA) UpdateLib(ctx context.Context, libID, libName, merchantID string) error {
	return dao.LocalQALib{}.Update(ctx, dal.Use(mysql.NewDB()), libID, libName, merchantID, -1)
}

// DeleteLib 删除词库
func (LocalQA) DeleteLib(ctx context.Context, libID, merchantID string) error {
	ids, _ := dao.ObjAssociation{}.GetAssociationID(ctx, dal.Q, define.AssociationLocalQA, "", libID)
	if len(ids) > 0 {
		return errorx.New("词库正在使用中,不能删除", -1)
	}
	// 采用事务
	q := dal.Q
	err := q.Transaction(func(tx *dal.Query) error {
		// 删除词库
		err := dao.LocalQALib{}.Delete(ctx, tx, libID, merchantID)
		if err != nil {
			return err
		}
		// 删除答案
		_, err = dao.LocalQAAnswer{}.BatchDelete(ctx, tx, libID)
		if err != nil {
			return nil
		}
		// 删除关键字
		_, err = dao.LocalQAKeyword{}.BatchDelete(ctx, tx, libID)
		if err != nil {
			return err
		}
		// 删除关联关系
		err = dao.ObjAssociation{}.Delete(ctx, tx, define.AssociationLocalQA, "", libID)
		if err != nil {
			return err
		}
		return nil
	})
	return err
}

// LibList 词库列表
func (LocalQA) LibList(
	ctx context.Context,
	name, merchantID string,
	page, limit int,
) (
	[]*models.LocalQALib,
	int64,
	error,
) {
	return dao.LocalQALib{}.List(ctx, dal.Use(mysql.NewDB()), name, merchantID, page, limit)
}

// LibDetail 词库详情
func (LocalQA) LibDetail(
	ctx context.Context,
	libID string, // 词库id
	merchantID string, // 商户详情
) (*dao.LocalQALibDetailType, error) {
	if libID == "" {
		return nil, errorx.New("词库id不能为空", -1)
	}
	if merchantID == "" {
		return nil, errorx.New("商户id不能为空", -1)
	}
	if !(dao.LocalQALib{}).Exists(ctx, dal.Q, libID) {
		return nil, errorx.New("词库不存在", -1)
	}
	// 获取词库详情
	detail, err := dao.LocalQALib{}.Detail(ctx, dal.Q, libID, merchantID)
	if err != nil || detail == nil {
		return nil, err
	}
	// 获取绑定的设备
	ids, _ := dao.ObjAssociation{}.GetAssociationID(ctx, dal.Q, define.AssociationLocalQA, "", libID)
	if len(ids) > 0 {
		detail.RobotID = ids
	} else {
		detail.RobotID = []string{}
	}
	return detail, nil
}

// ImportLib 导入问答
// libID 词库id
func (LocalQA) ImportQA(ctx context.Context, libID, merchantID string, reader io.Reader) (total int64, err error) {
	// 判断词库是否存在
	if !(dao.LocalQALib{}).Exists(ctx, dal.Q, libID) {
		return 0, errorx.New("词库不存在", -1)
	}
	xlsx, err := excelize.OpenReader(reader)
	if err != nil {
		return 0, errorx.New("文件有误", -1)
	}
	sheetName := xlsx.GetSheetName(0)
	rows, _ := xlsx.GetRows(sheetName)
	if len(rows) < 2 {
		return 0, errorx.New("词库不能为空", -1)
	}
	rows = rows[1:]
	// 删除原位置的词库-关键字+答案
	dao.LocalQAAnswer{}.BatchDelete(ctx, dal.Use(mysql.NewDB()), libID)
	dao.LocalQAKeyword{}.BatchDelete(ctx, dal.Use(mysql.NewDB()), libID)
	// 遍历每个品牌进行添加或更新
	keywordRecords := []*models.LocalQAKeyword{}
	answerRecords := []*models.LocalQAAnswer{}
	for _, row := range rows {
		if len(row) < 3 {
			continue
		}
		// 先创建一条答案record,文件的最后一列为答案
		answer := row[len(row)-1]
		answerRecord := &models.LocalQAAnswer{}
		answerRecord.ID, _ = helper.UUID{}.String()
		answerRecord.LibID = libID
		answerRecord.Answer = answer
		answerRecords = append(answerRecords, answerRecord)

		// 为该问题创建关键字，会用到中文分词，原始问题为第一列
		question := row[1]
		keywords := jieba.Jieba.Cut(question, true)
		keywords = lo.Uniq(keywords)
		for _, keyword := range keywords {
			// 排除掉不是词语的（单个字的不要）
			if utf8.RuneCountInString(keyword) <= 1 {
				continue
			}
			// 加入数组
			keywordRecords = append(keywordRecords, &models.LocalQAKeyword{
				LibID:    libID,
				AnswerID: answerRecord.ID,
				Keyword:  keyword,
			})
		}
	}
	// 批量更新插入-事务
	q := dal.Use(mysql.NewDB())
	err = q.Transaction(func(tx *dal.Query) error {
		// 批量插入答案
		err1 := dao.LocalQAAnswer{}.Add(ctx, tx, answerRecords, 300)
		if err != nil {
			return err1
		}
		// 批量插入关键词
		err2 := dao.LocalQAKeyword{}.Add(ctx, tx, keywordRecords, 300)
		if err2 != nil {
			return err2
		}
		// 更新词库数据
		total = int64(len(answerRecords))
		err3 := dao.LocalQALib{}.Update(ctx, tx, libID, "", merchantID, total)
		if err3 != nil {
			return err3
		}
		return nil
	})
	if err != nil {
		return 0, errorx.New("导入失败", -1)
	}

	return total, nil
}

// BindRobots 词库设备绑定
// libID 词库id
// robotIDs 设备id
// 设备<->词库
func (LocalQA) BindDevice(
	ctx context.Context,
	query *dal.Query,
	libID string, robotIDs []string,
) (
	[]string,
	error,
) {
	records := []*models.ObjAssociation{}
	if !(dao.LocalQALib{}).Exists(ctx, dal.Q, libID) {
		return []string{}, errorx.New("无效的词库", -1)
	}
	if libID == "" {
		return []string{}, errorx.New("词库id不能为空", -1)
	}
	// 删除旧的绑定
	dao.ObjAssociation{}.Delete(ctx, dal.Q, define.AssociationLocalQA, "", libID)
	// 增加新的绑定
	for _, robotID := range robotIDs {
		// 删除旧的设备绑定，只保留最近一次，避免一台设备绑定两个词库
		dao.ObjAssociation{}.Delete(ctx, dal.Q, define.AssociationLocalQA, robotID, "")
		// 新的绑定关系
		records = append(records, &models.ObjAssociation{
			AssociationType: int(define.AssociationLocalQA),
			ObjID:           robotID,
			AssociationID:   libID,
		})
	}
	if len(records) > 0 {
		ids, err := dao.ObjAssociation{}.Add(
			ctx,
			query,
			records,
			300,
			dao.Device{}.Exists,
			dao.LocalQALib{}.Exists,
		)
		return ids, err
	}
	return []string{}, nil
}

// Answer 本地词库答案
// 问题->分词->获取匹配的关键字[会找到多条答案id]->找到匹配关键字最多的答案
func (LocalQA) Answer(ctx context.Context, robotID, question string) string {
	// 答案关键词匹配计数
	matchCount := map[string]int{}
	// 获取设备匹配的词库
	ids, _ := dao.ObjAssociation{}.GetAssociationID(ctx, dal.Q, define.AssociationLocalQA, robotID, "")
	if len(ids) <= 0 {
		return ""
	}
	// 分词
	keywords := jieba.Jieba.Cut(question, true)
	keywords = lo.Uniq(keywords)
	validKeywordCount := 0 // 有效的关键字
	// 匹配计数
	for _, keyword := range keywords {
		// 忽略单个字的
		if utf8.RuneCountInString(keyword) <= 1 {
			continue
		}
		validKeywordCount++
		// 遍历词库，获取包含关键字的答案
		answerKeywords, _ := dao.LocalQAKeyword{}.List(ctx, dal.Q, ids[0], keyword)
		for _, ak := range answerKeywords {
			matchCount[ak.AnswerID]++
		}
	}
	// map分解为key和count的slice
	// 先找到最大的count，然后根据最大count的序号，找到对应序号的答案id
	answerKeyArr := []string{}
	answerCountArr := []int{}
	for k, count := range matchCount {
		answerKeyArr = append(answerKeyArr, k)
		answerCountArr = append(answerCountArr, count)
	}
	// 没有匹配的回复
	if len(answerKeyArr) == 0 {
		return ""
	}
	// 寻找最大计数的序号
	maxIndex := 0
	maxCount := 0
	for k, v := range answerCountArr {
		if v > maxCount {
			maxCount = v
			maxIndex = k
		}
	}
	// 关键字匹配没有达到50%的
	// 获取仅仅命令一个关键字的
	// 走大模型
	if (float64(maxCount)/float64(validKeywordCount))*100 < 30 || maxCount < 2 {
		return ""
	}
	// 获取并返回答案内容
	answer, _ := dao.LocalQAAnswer{}.Detail(ctx, dal.Q, answerKeyArr[maxIndex])
	if answer != nil {
		return answer.Answer
	}
	return ""
}
